obj-c swift

2016-07-28 21:39:52 rhythmkay 阅读数 2726

目录

前言

当我们开始接触一门新语言时,我们难免避免不了类型和基本语法规则。我们会急切的想知道如何用新的语法规则写我们原先所熟知的语句。本文旨在对于Object-C和Swift做一些基本的对比。通过阅读本文,您能快速的了解Swift3.0和Object-C的一些差别。

  • 基本类型
    同所有的面向对象语言一样,Swift里也只有两种类型:

    1.值类型
    2.引用类型

    值类型包括struct,enum;
    引用类型主要是class,嗯还有闭包也是引用类型哦。

  • 代码不再需要分号结尾

    Swift代码单行结束,不需要强制加分号,但也可以选择加

  • 类型推断

    Swift可以自动根据上下文为一个变量推断出其类型;

  • 强类型语言

    尽管Swift可以支持类型推断,可以用var来声明变量。但Swift是强类型语言。这样意味着,只要涉及不同类型之间的运算,都需要进行强制类型转换

  • 变量、常量定义

    let constant name : type = expression
    var variable name : type = expression

在开始所有介绍前,我们先看简单的几行代码.大家采一些预期结果是什么?

    var num=3.0
    var integer=6
    print(num+integer) //输出结果是?

结果:编译不过。因为Swift是一个强类型语言


从Object-C向Swift过渡

接下来将会从Protocol,block,GCD等大家在Object-C里熟知的内容开始,一步步过渡到Swift3.0。

**1.protocol
2.block
3.GCD
4.property
5.函数
6.extension,category
7.可选类型?与??
8.类型判断与转换
9.id类型与Any,AnyObject,nil
10.typedef与typealias
11.宏定义
12.混合编程
13.API设计与函数命名对比**

1.protocol

Swift也和OC一样有protocol,但不同的是Swift的protocol可以被class,struct,enum实现,甚至还能进行extension扩展。
参考:Protocols

  • Protocol以及optional protocol定义

//Object-C
@protocol RefreshHeaderDelegate <NSObject>
@optional
   - (void)willBeginRefresh:(PullRefreshHeader*)header;
   - (void)didRefresh:(PullRefreshHeader*)header;
@required
   - (void)didFinishRefresh:(PullRefreshHeader*)header;
@end

//Swift
@objc protocol RefreshHeaderDelegate {
    @objc optional func willBeginRefresh(header:PullRefreshHeader) -> Void
    @objc optional func didRefresh(header:PullRefreshHeader) -> Void
}

//加了classprotocol才能被使用在类的delegate模式中,因为protocol不加class标记则还可以被enum,struct实现
protocol RefreshHeaderDelegate :class{
    func willBeginRefresh(header:PullRefreshHeader) -> Void
    func didRefresh(header:PullRefreshHeader) -> Void
}
  • 定义了optional方法后,如何判断delegate是否实现了该方法?
//Object-C
if([self.delegate respondsToSelector:@selector(willBeginRefresh:)]){
  //执行delegate的方法
  [self.delegate willBeginRefresh:self];
}

//Swift中我们可以更优雅的实现,optional是很方便的
self.delegate?.willBeginRefresh?(header: self)

2.block

Swift中任何两个{}之间的代码都算是闭包,因此函数也是一种特殊的闭包。
参考:closure-expressions-in-swift

  • 闭包定义
//Swift闭包定义
{ (obj:AnyObject) ->Void in
  //闭包内容开始
  print(obj)
  pring($0)
  //可以用$<index>来指向闭包的形参,下标从0开始
}
  • 如何避免闭包中的循环引用?
//Object-C
__weak __typeof(self) __weak_self = self
[button onClicked:^(MttTopBannerButton * button) {
    MttBottomBanner *banner=[[MttBottomBanner alloc] init];
    [__weak_self.view addSubview:banner];
    [banner show];
    button.hidden=YES;
 }];

 //Swift更优雅
button.onClicked({
    [weak self]
    (button:MttTopBannerButton) ->Void in
    var banner = MttBottomBanner()
    self?.view.addSubview(banner)
    banner.show()
    button.hidden=true
})
  • weakself,strongself
//Swift
DispatchQueue.main.sync(execute: {
    [weak self]
    (_:AnyObject?) ->Void in
    self?.draw()
    self?.color=UIColor.white()
    DispatchQueue.main.sync(execute: {
        if let strongSelf=self {
            strongSelf.lock.lock()
            strongSelf.layer.contents=strongSelf.display?.cgImage
            strongSelf.lock.unlock()
        }
    })
})

//请思考为何此处我是NSLock来加锁?
//因为Swift里没有@synchonized关键字的替代品了啊,需要的话,得自行调用GCD接口实现了.

//Object-C
__weak __typeof(self) __weak_self = self
dispatch_async(dispatch_get_main_queue(),^{
    [self draw];
    self.color=[UIColor whiteColor];
    __typeof(__weak_self) strongSelf = __weak_self
    dispatch_async(dispatch_get_main_queue(),^{
        [strongSelf.lock lock];
        strongSelf.layer.contents=strongSelf.display.CGImage;
        [strongSelf.lock unlock];
    })
});

3.GCD

Grand Dispatch Queue在Object-C中是一组C语言接口,虽然在swif1,swift2中仍保留了这种用法习惯,但它毕竟不太符合一门强类型与面向对象的语言的要求(就像这些人取消了++,–运算符一样,无时无刻不再想着摒弃老式的C语言编程思维),于是Swift3中GCD终于也面向对象了.

  • dispatch_async
//Swift
DispatchQueue.main.async(execute:{blk(nil)})

//Object-C
dispatch_async(dispatch_get_main_queue(),^(id obj){
});
  • dispatch_after
//Object-C
- (void)performBlockOnMainThread:(void (^)())block afterDelay:(CGFloat)delay
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        block();
    });
}

//block参数可为空,因为是optional
func performOnMainThread(_ block:AsyncLoaderTask? ,delay:Double) -> Void {
    if let blk=block {
        let time=DispatchTime.now()+delay
        //DispatchWorkItem相当于之前的dispatch_queue_t所以直接用
        DispatchQueue.main.after(when:time,execute:{blk(nil)})
    }
}

4.Property属性

在Swift中类里定义了的变量或常量即是属性

属性分为2种:

  • 存储型属性
    跟Object-C的property基本差不多
  • 计算型属性
    比较特别,并不是用于真正的存储一个值或变量,而是提供了getter和setter方法来间接的获取其他属性或变量的值。其本身的存在价值是依赖于其它属性或变量的存在

1.属性定义
2.属性观察器
3.getter,setter
4.delegate模式
5.普通属性
6.lazy标记
7.optional属性
8.completion block模式
9.初始化

typealias AsyncLoaderTask = (_:AnyObject?) ->Void
class PullHeaderTable: UITableView {

    // MARK: Public Properties
    //////////////
    //1.标准属性定义方式
    //常量属性,以及属性定义的两种方式,可以隐身让Swift自行推断类型,也可以明确指定类型
    let source=PullHeaderTableSource()
    let src : PullHeaderTableSource = PullHeaderTableSource()
    var s : PullHeaderTableSource = PullHeaderTableSource()

    //////////////
    //2.property观察器
    var data : [AnyObject]?{
        didSet{
            source.data=data
            print(oldValue)
        }
        willSet{
            print(newValue)
        }
    }

    //////////////
    //3.getter,setter,通过getter返回的,叫做计算型属性
    var src : PullHeaderTableSource  {
        get{
            return PullHeaderTableSource()
        }
        set{
            print(newValue)
        }
    }

    //////////////
    //4.delegate模式
    weak var tableDelegate : PulllHeaderTableDelegate?

    //////////////
    //5.普通属性,必须在init方法里最先被初始化
    var header : UIView

    //////////////
    //6.lazy标记,该属性只会在被调用时自动调用初始化赋值,而不是在init之前就初始化了,thread unsafe
    lazy var footer = UIView()

    //7.optional属性
    var listener : AnyObject?

    //8.optional block属性(和OC的completion block模式)
    var completion : AsyncLoaderTask?

    //////////////
    //9.对于非optional的属性,在init时必须要初始化,否则会出错
    //init初始化与OC的不一样,是先把当前实例的非optional属性初始化完毕,然后再调用父类的init
    //原因在于Swift定义了的任何类型property编译器都不会给默认值,因此非optional property必须在init中有优先初始化

    init(frame:CGRect) {
        header=UIView()
        super.init(frame:frame, style:UITableViewStyle.plain)
        self.onCreate()
    }

    required init?(coder aDecoder: NSCoder) {
        header=UIView()
        super.init(coder: aDecoder)
        self.onCreate()
    }

    override init(frame:CGRect,style:UITableViewStyle){
        header=UIView()
        super.init(frame: frame, style: style)
        self.onCreate()
    }

    deinit {
        self.dataSource=nil
        self.delegate=nil
    }

    // MARK: Private Functions
    private func onCreate() -> Void {
        self.scrollsToTop=false
        self.separatorColor=UIColor.clear()
        self.separatorStyle=UITableViewCellSeparatorStyle.none
        self.contentOffset=CGPoint.zero
        self.delegate=source
        self.dataSource=source
    }
}

5.函数

使用 func 来声明一个函数,使用名字和参数来调用函数。使用 -> 来指定函数返回值的类型。
参考:Functions

1.如何让函数返回多个返回值?
2.如何传入1个或N个参数
3.重构->函数内嵌套函数
4.函数类型与函数返回值,函数参数

Swift的函数相比较于OC发生了很多很好的改进,我们可以更自由的使用定义函数了.
同时还加入了函数类型。
参考:函数类型

class App: NSObject {

    func test() {
        print(calculate(scores: [0,1,2,3,4,5]))
        print(sumOf(numbers: 0,1,2,3,4,5))
        runAnotherFunction(longlongFunction)
    }

    // MARK:Function Usage
    //1.使用元组返回多个返回值
    //下述函数从数组中返回最小,最大,求和.返回值是一个元组.
    //其中调用了Swift里的min,max函数,全局泛型函数,用于比较大小返回结果.
    //min,max函数类似于OC宏定义的MIN(),MAX()
    func calculate(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
        var vmin=scores[0]
        var vmax=vmin
        var sum : Int = 0
        for val in scores {
            vmin=min(vmin, val)
            vmax=max(vmax, val)
            sum+=val
        }
        return (vmin,vmax,sum)
    }

    //2.传入可变个数的参数
    func sumOf(numbers: Int...) -> Int {
        var sum=0
        for val in numbers {
            sum+=val
        }
        return sum
    }

    //3.嵌套函数,用于部分代码的重构,其实也可以里面定义一个block一样的。不过比OC多了个选择
    func longlongFunction() {
        var x=10
        //猜猜看 _ 这里下划线的作用?看看如何调用的你就会明白了
        func increament(_ val:Int) -> Int {
            return val+1
        }
        print(increament(x))
        func add() {
            //x++
            //Swift 3.0不支持++,--了哦
            x=x+1
        }
        add()
    }

    //4.函数作为参数,当然函数也可以作为返回值的.函数类型
    func runAnotherFunction(_ fun: () -> ()) {
        fun()
    }
}

6.extension,category

Swift中没有category的概念,因为Swift中的extension基本涵盖了所有原先Object-C中需要的一切

1.添加计算型属性和计算型静态属性
2.定义实例方法和类型方法
3.提供新的构造器
4.定义下标[]
5.定义和使用新的嵌套类型
6.支持协议

参考:下标运算符

extension String {
    func toHex() -> String {
        return ""
    }
}

protocol AppProtocol: class {
    func app() -> Void
}

class App: NSObject {
    var data : Array = Array<Int>()
    static var instance = App()
    private override init() {
        for i in 0...8 {
            self.data.append(i)
        }
    }
}

extension App: AppProtocol {

    //////////////
    //1.添加计算性属性,存储型的不可以啊
    var hashCode : Int {
        return Int(Date.timeIntervalSinceReferenceDate)+8
    }

    //非法,存储性属性不能再extension里存在
    //var newProperty : Int = 0

    //////////////
    //2.添加新的构造函数
    convenience init(_ id:Int, time:Int) {
        self.init()
    }

    //////////////
    //3.函数或实现协议
    func app() -> Void {
        print("app")
    }

    //////////////
    //4.为对象实现下标运算符
    subscript(index: Int) -> Int {
        get{
            return self.data[index]
        }
        set{
            self.data[index]=newValue
        }
    }

    //////////////
    //5.嵌套类型,class,enum,也就是相当于定义内部类
    class UI : NSObject {
    }
}

func testApp() -> Void {
    let app = App.instance
    app[0]=1
    print(app[1])
}

7.可选类型?与??

可空链式调用(Optional Chaining)
这个名词太专业了,我们就简单的来谈谈吧

定义属性或变量时,如果要允许一个属性为空即nil,像Object-C一样的话。那么我们需要制定其位optional类型
只需定义结束末尾在加一个?符号即可,如下

var obj : AnyObject?
//定义了一个相当于Object-C的 id obj=nil;东西

对于optional类型的变量或方法(如optional protocol method)我们在调用时需要把optional类型的值/对象取出来,这一步叫做unwrap;反之定义时就叫wrap;
通过?来unwrap或者直接利用!来强制unwrap(强制的前提是你能确保变量是有有效值的),如下

var str : String?
str="value"
//前面已经为str赋值过了,所以我们是已知强制unwrap是不会有异常的
if str!.characters.count>0 {
}

//如果签名没有为str赋值,或者不确定有值与否,需要尝试unwrap
if str?.characters.count>0 {
}

var count : Int
count=str?.characters.count ?? 0
print(count)

大家是否在好奇上述的??符号到底是什么意思呢?很简单,如果你理解选择表达式,那么可以把它看做对于optional类型的选择表达式吧
等价于如下代码

if str != nil {
    count=str!.characters.count
}
else
{
    count=0
}

8.类型判断与转换

1.在Object-C中我们经常会对一个id类型或者不确定具体类型的实例对象做类型判断,如isKindOfClass;在Swift中我们可以更简单来实现

参考:Type Casting

is类型检查运算符

//Object-C判断类型
UIView *v=XXX;
if([v isKindOfClass:[UIWindow class])
{
}

//Swift
var v=XXX
if v is UIWindow {
}

2.在Object-C中我们要做类型转换可以直接使用C语言的方式来强制类型赋值,但在Swift中需要用as运算符实现

as 用于向上转型(upcasts)
as? 和as! 用于向下转型(Downcasting)

备注:
1.向上转型:由派生类转为父类对象
2.向下转型:有父类对象转为具体子类对象

当不确定类型转换是否成功时,用类型转换的条件形式( as? )
as?如果失败会返回nil
当确定一定是时,用强制形式式( as! )

//Object-C
for(UIViewController *ct in controllers)
{
    if([ct isKindOfClass:[MttBaseViewController class]])
    {
        NSLog(@"MttBaseViewController");
    }
    else if([ct isKindOfClass:[MttRootViewController class]])
    {
        NSLog(@"MttRootViewController");
    }
}

//Swift
for ct in controllers {
    if let mtt = ct as? MttBaseViewController {
            print("MttBaseViewController")
    } else if let nav = ct as? MttRootViewController {
            print("MttRootViewController")
    }
}

9.id类型与Any,AnyObject,nil

Any 和 AnyObject 是 Swift 中两个妥协的产物;

AnyObject 可以代表任何 class 类型的实例
Any 可以表示任意类型,甚至包括方法 (func) 类型

AnyObject用于在Swift中替换Object-C的id类型;
Swift中编译器不会对 AnyObject 实例的方法调用做出检查,甚至对于 AnyObject 的所有方法调用都会返回 Optional 的结果。因此使用AnyObject实例之前我们务必需要检测对象是否存在和类型有效性。
参考:ANY 和 ANYOBJECT

nil:
Swift中的nil与Object-C的nil是不一样的。Object-C中,nil是一个指针指向一个不存在的object。在Swift中,nil不是指针,而是对于特定类型的缺省值。任何optional类型都可以设置为nil,而不仅仅是对象类型;
optional类型变量的值默认就是nil;

10.typedef与typealias

typealias相当于Object-C中的typedef,用来为已经存在的类型重新定义名字。通过命名,可以使代码变得更加清晰。
参考:swift-typealias,typelias

//重命名闭包,和Object-C一样
typealias AsyncLoaderTask = (_:AnyObject?) ->Void
func submit(_ block:AsyncLoaderTask?,completion:AsyncLoaderTask?) -> Void {
    if let blk=block {
        self.queue.async(execute:{
            blk(nil)
            if let cmp=completion {
                cmp(nil)
            }
        })
    }
}

//protocol组合
protocol changeName{
    func changeNameTo(name:String)
}
protocol changeSex{
    func changeSexTo(sex:String)
}
typealias changeProtocol = protocol<changeName,changeSex>
struct Person:changeProtocol{
  func changeNameTo(name:String){
  }
  func changeSexTo(sex:SEX){
  }
}

//swift中很多类型别名即是typealias定义的
public typealias AnyClass = AnyObject.Type
public typealias NSInteger = Int

11.宏定义

很抱歉,Swift中终于不支持宏定义了。
Object-C中的宏暴露到Swift中的话,只有简单展开宏会被直接变为同名常量。
参考:converting-complex-objective-c-macros-swift-functions,Objective的宏到swift

//Object-C
#defien MTT_DEBUG 1

//Swift
let MTT_DEBUG = 1 
//以上二者等价

其余复杂的宏定义以及宏嵌套都无法被翻译到Swift里,也无法在Swift中调用或使用。
所以要使用宏,只能在Object-C中使用.

12.混合编程

处于某些目的,我们还是有必要在Swift代码中使用Object-C,或者在Object-C代码中装一下样子,搞点Swift。那这时就涉及到混合编程了。
参考:混和编程

1.OC中使用Swift
在工程的 Build Settings 中把 defines module 设为 YES.即可.(如需必要,再把把 product module name 设置为项目工程的名字。理论上不需要了,XCode 8.0已经默认是这样了)
最后一步,在你的OC文件中导入 ProjectName-Swift.h.即(productModuleName-Swift.h)

2.Swift中使用OC
Swift代码引用OC,需依靠 Objective-C bridging header 将相关文件暴露给Swift。
创建 Objective-C bridging header 有两种方法

a.当你在Swift项目中尝试创建OC文件时,系统会自动帮你创建 Objective-C bridging header
b.自己创建 Objective-C bridging header
File > New > File > (iOS or OS X) > Source > Header File
切记,名字 一定要 是 项目工程名-Bridging-Header.
然后还有一步,在项目的 Build Settings 选项里,要确保Swift Compiler 选项里有这个 Bridging Header 文件的设置,路径必须指向文件本身,而不是目录!

13.API设计与函数命名对比

篇幅原因,这里就不copy&&paste了,大家可以自行看原文或者译文。这是一篇很好的API设计指导,终于可以抛弃Object-C的冗长命名了。
参考:Swift API设计 , 译文


参考资料

结语

如果你在一门语言上,可以看到很多让你曾痴狂,压抑,热爱,也失望的特性时。那就一定是Swift了。
本文比较了Swift3.0和Object-C在开发中主要的一些差别,限于篇幅(已经很长了)与作者本人认知能力,并未对Swift中更多特性做介绍或深究。如有疏漏或错误还望指正。
Swift就像一个四不像,因为var而像javascript,因为@objc而又保留了Object-C特性,因为内部类而多了点Java的影子,因为deinit而带了些c++的气味。但无论如何,Swift必定是将来!
参考:About Swift

2017-09-30 11:37:50 oiken 阅读数 682

转载自: https://stackoverflow.com/questions/32541268/can-i-have-swift-objective-c-c-and-c-files-in-the-same-xcode-project/32546879#32546879

Can I have Swift, Objective-C, C and C++ files in the same Xcode project?




Can all 4 languages be used in the same project at all, and if so how?

There are similar questions in the flavor: Can I mix Swift with C++? Like the Objective - C .mm filesto which the accepted answer is no.

Using Bridging Header adequately, .h that do not contain C++ statements, Objective-Cwrappers when .h do contain C++.mm files to do the actual wrapping of C++ classes, and .swift, can the 4 languages (5 if you include Objective-C++) build and link into a single executable?


YES.

You can mix SwiftCC++Objective-C & Objective-C++ files in the same Xcode project.

C

// Declaration: C.h
#ifndef C_h
#define C_h
#ifdef __cplusplus
extern "C" {
#endif
    void hello_c(const char * name);
#ifdef __cplusplus
}
#endif
#endif /* C_h */

// Definition: C.c
#include "C.h"
#include <stdio.h>
void hello_c(const char * name) {
    printf("Hello %s in C\n", name);
}

C++

// Declaration: CPP.hpp
#pragma once
#include <string>
class CPP {
public:
    void hello_cpp(const std::string& name);
};

// Definition: CPP.cpp
#include "CPP.hpp"
#include <iostream>
using namespace std;
void CPP::hello_cpp(const std::string& name) {
    cout << "Hello " << name << " in C++" << endl;
}

Objective-C wrapper for C++

// Declaration: CPP-Wrapper.h
#import <Foundation/Foundation.h>
@interface CPP_Wrapper : NSObject
- (void)hello_cpp_wrapped:(NSString *)name;
@end

// Definition: CPP-Wrapper.mm
#import "CPP-Wrapper.h"
#include "CPP.hpp"
@implementation CPP_Wrapper
- (void)hello_cpp_wrapped:(NSString *)name {
    CPP cpp;
    cpp.hello_cpp([name cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end

Objective-C

// Declaration: Objective-C.h
#import <Foundation/Foundation.h>
@interface Objective_C : NSObject
- (void)hello_objectiveC:(NSString *)name;
@end

// Definition: Objective-C.m
#import "Objective-C.h"
@implementation Objective_C
- (void)hello_objectiveC:(NSString*)name {
    printf("Hello %s in Objective-C\n", [name cStringUsingEncoding:NSUTF8StringEncoding]);
}
@end

Objective-C++

// Declaration: Objective-CPP.h
#import <Foundation/Foundation.h>
@interface Objective_CPP : NSObject
- (void)hello_objectiveCpp:(NSString *)name;
@end

// Definition: Objective-CPP.mm
#include <iostream>
#import "Objective-CPP.h"
using namespace std;
@implementation Objective_CPP
- (void)hello_objectiveCpp:(NSString *)name {
    cout << "Hello " << [name cStringUsingEncoding:NSUTF8StringEncoding] << " in Objective-C++\n";
}
@end

Swift

// Declaration & definition: Swift.swift
func hello_swift(_ name: String) {
    print("Hello \(name) in Swift")
}

Bridging-Header.h

Cannot import CPP.hpp header file, not because of it's naming convention, but because it contains the class keyword.

#import "C.h"
#import "CPP-Wrapper.h"
#import "Objective-C.h"
#import "Objective-CPP.h"

Invocation from Swift

// Invoke C
hello_c("World".cStringUsingEncoding(NSUTF8StringEncoding))

// Can't Invoke C++ without a wrapper
// CPP().hello_cpp("World".cStringUsingEncoding(NSUTF8StringEncoding))
// Invoke C++ through Objective-C
CPP_Wrapper().hello_cpp_wrapped("World")

// Invoke Objective-C
Objective_C().hello_objectiveC("World")

// Invoke Objective-C++
Objective_CPP().hello_objectiveCpp("World")

// Invoke Swift
Swift().hello_swift("World")

.h (Headers)

(See item 3 in this Stack Overflow answer)

.h: this is the tricky part, since they are ambiguously used for all flavors of C, ++ or not, Objective or not. When a .h does not contain a single C++ keyword, like class, it can be added to the ...Bridging-Header.h, and will expose whatever function the corresponding .c or .cpp functionalities it declares. Otherwise, that header must be wrapped in either a pure C or Objective-C API.

Output

Hello World in C
Hello World in C++
Hello World in Objective-C
Hello World in Objective-C++
Hello World in Swift

Comments

Cy-4AH:

Yes. You only need wrap C++ into C or Objective-C to use in Swift.

Tommy

Indeed, I have a project that does exactly that. C++ for the thrust of the abstract cross-platform model stuff with some C parts underneath; Objective-C to wrap the C++ classes for Swiftpurposes, Swift to bind all that to a subclass of NSDocument, with some custom views that interrogate the C stuff.

MaddTheSane

Added the extern "C" wrapper as per your excellent suggestion. To invoke the C method void hello_c(const char * name) from C++ method hello_cpp(const std::string& name), add #include "C.h" and call hello_c(name.c_str());.

Keith Adler

The new SO-32541268: Now with parameters!


2018-08-06 09:12:44 weixin_34109408 阅读数 36

Obj-C 扩展

  • 在OC中经常会使用扩展,如下在.m文件中。

1. @interface 类名后面加()

2. 扩展中自动生成实现【get、set方法 以及 声明一个成员变量,成员变量命名会在属性名的前面加一个_】

3. oc中定义私有属性的方式。【那私有方法呢??直接写实现呀,还要定义干嘛?】

#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) UIView *view1;

@end

@implementation ViewController

- (void)viewDidLoad {
    _view1 = [[UIView alloc]init];
    [self.view addSubview:_view1];
}

@end

复制代码
<?php 
echo "Hello World!"; 
?> 
复制代码

Obj-C 中的分类

  • 我这里新建了一个Person对象。里面什么都没有然后写了个Person的分类Person(addition)
  • 分类中只能定义方法
  • ()必须有名称

.h文件

#import "Person.h"

@interface Person(addition)
@property (nonatomic, assign) int year;
@end

复制代码

.m文件

#import "Person+addition.h"

@implementation Person(addition)

@end

复制代码

ViewController中创建对象并且进行打印。

#import "ViewController.h"
#import "Person+addition.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p1 = [[Person alloc]init];
    p1.year = 11;
    NSLog(@"%d",p1.year);
}
复制代码

这个异常信息,它通常是消息接收者找不到对应的@selector()方法。意思是get set方法都没有。

其实Xcode已经提示了。没有定义set 和get 方法【其实连申明都没有因为_year找不到~~】。我感觉这里报错会比较合适。?

但是呢。? 使用@synthesize year手动创建 set year ivar呢?会报错。【所以想要扩展属性还是乖乖的用runtime吧】

可以写@property (nonatomic, assign) int year;但是没有定义属性,只能写set 和get方法那如何应用呢?

如何使用。

  • Person.h
#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, assign) int year;
@end
复制代码
  • Person.m文件文件没有内容

  • Person+addition.h文件

#import "Person.h"

@interface Person(addition)
@property (nonatomic, assign) int theYear;

-(int)theYear;
-(void)setTheYear:(int)theYear;

@end
复制代码
  • Person+addition.m文件
#import "Person+addition.h"

@implementation Person(addition)

-(int)theYear{
    return self.year + 1;
}
-(void)setTheYear:(int)theYear{
    self.year = theYear;
}

@end
复制代码

其实就是用 set get 方法进行一些操作。比如对里面属性赋值呀。运算等。

  • ViewController.m 中使用
#import "ViewController.h"
#import "Person+addition.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    Person *p1 = [[Person alloc]init];
    p1.theYear = 1;
    NSLog(@"myYear:%d",p1.year);
    NSLog(@"year:%d",p1.theYear);
}


@end
复制代码

打印结果

Swift

  • Swift中是没有分类的。只有扩展哦。
  • 和OC中的分类又点类似,不能定义属性,用起来像是属性但是呢 其实就是get 和set方法。但是本身不是属性。
  • 可以在扩展中定义方法。
  • 呢么看Swift中的扩展简直就是简单粗暴。
import UIKit

class Person: NSObject {
    var myYear:Int = 0;
}


extension Person{
    var year:Int{
        get{
            return myYear;
        }
        set{
            
            myYear = 22
        }
    }
    
    func log() {
        print("打印下")
    }
}

复制代码

Person的使用

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let p1 = Person()
        print(p1.year);
    }

}
复制代码

Swift中的扩展用法和OC的分类类似。而OC的扩展基本就是用于创建私有属性。 由于Swift中私有属性都是 private来申明的所以就没了?


最后 OC中的分类中的结构体有几个属性?

这里不写代码了,其实在我们经常使用的代码里就有了。说一个现象可能就想到了,那就是不能对frame的中的属性直接复制,这是为啥呢?

char *category_name                        OBJC2_UNAVAILABLE; // 分类名
char *class_name                             OBJC2_UNAVAILABLE; // 分类所属的类名
struct objc_method_list *instance_methods    OBJC2_UNAVAILABLE; // 实例方法列表
struct objc_method_list *class_methods       OBJC2_UNAVAILABLE; // 类方法列表
struct objc_protocol_list *protocols         OBJC2_UNAVAILABLE; // 分类所实现的协
复制代码
  • 原来frame是在分类中,呢么按照刚说的,先不管结构体。呢么肯定了我们使用的是get 和set 方法,而不是属性。
  • 而CGRect是一个结构体。
  • 所以我们只能进行set方法了。就有了我们平时的写法 self.view.frame = CGRectMake(0, 0, 0, 0);所以呀结构体里是没有属性的,我们只是把结构体当做参数拿来对UIView中的一些属性进行了操作。

2016-01-24 12:15:44 IT_DS 阅读数 1528

嵌套函数

函数分为全局函数和局部函数,前面已经介绍了全局函数,下面介绍下局部函数,定义在函数体内部的函数可以成为嵌套函数。
注意:嵌套函数对外是隐蔽的,只能在其封闭函数内有效,嵌套函数只能在其封闭函数内有效,其封闭函数也可以返回嵌套函数,以便于程序在其他作用域内使用嵌套函数。

func Math(#type:String)->(Int)->Int
{
 func square(val:Int)->Int
 {
  return val*val
}
  func cube(val:Int)->Int
  {
    return val*val*val
  }
  switch(type)
  {
    case "square":
    return square
    case "cube":
    return cube
    default:
    print("没有该函数")
  }
}
var mathFunc=Math(type:"cube")//先得到cube函数,调用权利给mathFunc
print(mathFunc(5))调用cube函数,输出125
var mathFunc=Math(type:"square")//先得到square函数,调用权利给mathFunc
 print(mathfunc(5))调用square函数,输出25

闭包

闭包是一种新的语法,跟Object-C的代码块一样,更加灵活性。

其实函数也可以算的上闭包处理

* 全局函数是一个有名称、但不会捕捉任何值的闭包。
* 嵌套函数是一个有名字、可以捕获封闭函数体的值的闭包。
* 闭包表达式是一个简单的、可以捕获封闭函数体的值的匿名闭包。

    func Math(#type:String)->Int->Int
    {
     switch(type)
     {
     case "square":
     return {(val:Int)->Int in
      return val*val
  }
     case "cube":
     return {(val:Int)->Int in
     return val*val*val
     }
     default:
       print("没有该函数")
         }
        }
    var mathFunc=Math(type:"cube")
    print(mathFunc(5))

闭包与嵌套函数存在的区别
* 定义闭包无须func关键字,无须制定函数名。
* 定义闭包需要额外使用in关键字。
* 定义闭包的第一个花括号要移到形参列表的圆括号之前。
标准语法如下:

    {
    (形参列表)->返回值类型 in
    //核心代码 
    }

闭包的本质是更加灵活的代码块,因此完全可以将闭包赋值给变量或直接调用闭包。

var square={(val:Int)->Int in
return val*val
}
print(square(5))
var result=(#base:Int,#exponent:Int)->Int in
var result = 1
for i in 1...exponent
{
 result *=base
}
return result
}(4,3)
print(result)

上面两个闭包函数,第一个是先声明再调用,第二个是声明后直接调用
注意目前Swft中闭包的外部形参名没有任何意义。

几种简化方法

省略形参列表、返回值类型
//使用闭包表达式,由于程序定义square变量的类型
//所以swift可以推断闭包表达式的形参列表、返回值类型

    var square:(Int)->Int ={(var) in return val*val}

//省略形参类型后,也可省略形参列表的圆括号,即可简化成

var square:(Int)->Int={var in return val*val}
print(square(5))//输出25
省略return

如果闭包表达式只有一行代码,并且这行代码的返回值将作为闭包表达式的返回值,那么可以省略

2017-09-07 10:29:21 chen_xi_hao 阅读数 643

删除线,有几种实现思路
一种是用富文本实现
一种是继承uilabel,重写draw方法,画出来
一种是用分类(swift用拓展)实现
…..

这里写图片描述

在这里我觉得画出来是最好看的,所以,我采用了第二种,画出来

obj-c:

#import <UIKit/UIKit.h>

@interface StrickoutLabel : UILabel

@end
#import "StrickoutLabel.h"

@implementation StrickoutLabel

-(void)drawRect:(CGRect)rect{
//    // 调用super的drawRect:方法,会按照父类绘制label的文字
    [super drawRect:rect];

    /**
     * 因为是在xib布局,利用的是约束,所以不能用self.frame.size.height得到高度,这个值是没有值的,
     * 这时候需要用CGRectGetHeight(self.frame)来得到高度。
     */

    // 1 获取上下文
    CGContextRef context = UIGraphicsGetCurrentContext();

    // 2 设置线条颜色
    [self.textColor setStroke];

    // 3 线的起点
    CGFloat y = CGRectGetHeight(self.frame) /2;
    CGContextMoveToPoint(context, 0, y);
    // 4 短标题,根据字体确定宽度
    CGSize size = [self.text sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:self.font,NSFontAttributeName, nil]];
    // 5 线的终点 所以换行出问题了
    CGContextAddLineToPoint(context, size.width, y);
    // 6 最后渲染上去
    CGContextStrokePath(context);


}

@end

相应的,转换成swift代码的时候,代码如下

//
//  StrickoutLabel.swift
//  ego
//
//  Created by xihao on 2017/8/25.
//  Copyright © 2017年 yidont. All rights reserved.
//

import Foundation
import UIKit

class StrickoutLabel2 :UILabel{

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)

//        // 1 获取上下文
//        CGContextRef context = UIGraphicsGetCurrentContext();
//        
//        // 2 设置线条颜色
//        [self.textColor setStroke];
//        
//        // 3 线的起点
//        CGFloat y = CGRectGetHeight(self.frame) /2;
//        CGContextMoveToPoint(context, 0, y);
//        // 4 短标题,根据字体确定宽度
//        CGSize size = [self.text sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:self.font,NSFontAttributeName, nil]];
//        // 5 线的终点 所以换行出问题了
//        CGContextAddLineToPoint(context, size.width, y);
//        // 6 最后渲染上去
//        CGContextStrokePath(context);

        let context = UIGraphicsGetCurrentContext()!

        self.textColor.setStroke()

        let y : CGFloat = self.frame.height/2

        context.move(to: CGPoint.init(x: 0, y: y))

        let size = (self.text! as NSString).size(attributes: [NSFontAttributeName:self.font])

        context.addLine(to: CGPoint.init(x: size.width, y: y))

        context.strokePath()

    }

}

以上代码测试过了,没问题

QQ:361561789
有事可以直接加Q联系