2015-02-04 19:35:51 qq_24954629 阅读数 316
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10086 人正在学习 去看看 栾斌

KVC key-value Coding, 键值编码.和点语法一样是赋值的一种方法, 今天我们一起来看看 KVC在数据解析时应用

首先,创建一个继承与NSObject的类,在类里面创建属性,并且属性要和我们解析出来的数据的key值一样,属性类型也要一一对应

这是JSON解析工具解析出来的数据,我们首先应该去分析数据结构,我么需要的数据都在 items里.我们在类中的所需要写的属性和上面我们需要的数据的数据类型一致

但是其中,id和系统的名字重名,所以我们就应该自定义一个名字,但是如果我们自定义,在使用KVC的时候,系统并不能分辨,所以我们还要在.m中重写KVC方法,我们使用的是UnderfindKey,就是不能找到的key

在赋值的时候,我们使用KVC系统会自己根据key值进行赋值

在使用的时候,和我们用其他方式赋值一样.当然这只是简单的使用方法,在以后我们会对KVC进行补充

不过,并不是我们在赋值的时候一定要使用KVC赋值,我们的目的是赋值,之于途径和方法,当然是多种多样的

2016-12-26 22:20:41 feng2qing 阅读数 1958
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10086 人正在学习 去看看 栾斌

0.创建一个类:Person.swift

//name属性是可选的,在需要的时候创建
//在手机开发中,内存非常宝贵,实际运行中很多属性不一定需要分配空间
var name: String?

1.重写构造函数,用字典为本类设置初始值

init(dict: [String: AnyObject]) {

    super.init()
    //使用 self 的方法 'setValuesForKeys' 之前,应该调用 super.initt(),否则会奔溃:Use of 'self' in method call 'setValuesForKeys' before super.init initializes self
    //KVC方法是 OC 的方法,在运行的时候给对象发送消息
    //执行'setValuesForKeys',首先要求对象已经实例化完成
    setValuesForKeys(dict)
}

1.1然在控制器中传值调用

let p = Student(dict: ["name": "小明" as AnyObject])
print(p.name)
//打印输出: Optional("小明")

2.在Person.swift中继续追加一个属性

//给基本类型属性初始化
//如果这样写:
//var age: Int?
//会抛出以下异常:
//Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<******> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key age.'
// - 意思是KVC会无法找到age的KEY,因为Int是一个基本数据类型的结构体
//所以要这么写,赋初始值    
var age: Int = 0

2.1去控制器中传值调用

let p = Student(dict: ["name": "小明" as AnyObject,"age": 18 as AnyObject])
print("\(p.name) \(p.age)")
//打印输出: Optional("小明") 18

3.试图在Person.swift添加一个私有的属性

//private var title: String?
//直接报异常:Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<****** > setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key title.
// - 如果是private属性,使用KVC设置的时候,程序会直接崩溃
// - Swift中被Private修饰的属性或方法,彻底禁止外部访问,OC中私有属性或者方法可以通过runtime访问到,所以此时无法使用private修饰
var title: String?

3.1在控制器中打印输出

let p = Student(dict: ["name": "小明" as AnyObject,"age": 18 as AnyObject,"title": "北京" as AnyObject])
print("\(p.name) \(p.age) \(p.title)")
//打印输出: Optional("小明") 18 Optional("北京")

总结:

1. 定义属性的时候,如果是对象,通常都是可选的(类后面加’?’)

- 在需要的时候创建

- 避免写构造函数,可以简化代码

2. 如果是基本数据类型,不能设置为可选的,而且要设置初始值,否则KVC崩溃

3. 若果需要使用KVC设置属性,属性不能是private的

4. 使用KVC方法之前,应该调用 super.init 保证对象实例化完成

2018-01-31 09:16:48 u011146511 阅读数 304
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10086 人正在学习 去看看 栾斌
===========kvc

KVC是OC特有的,KVC本质上是在运行时,动态向对象发送setValue:ForKey:方法,为对象的属性设置数值

因此,在使用KVC的方法之前,需要确保对象已经被正确实例化

在Swift中,如果属性是可选的,在初始化时,不会为该属性分配空间。

在OC中,基本数据类型就是保存一个值,不存在可选的概念

所以我们需要给可选的基本数据类型设置一个初始值,否则使用KVC就会报错。

如果想要在Swift中使用KVC,这个类必须要继承自NSOject,因为KVC是OC的东西。


KVC函数的调用顺序:

setValuesForKeysWithDictionary会按照字典中的key重复调用setValue:ForKey:函数

在重复调用的过程中,如果发现字典中对应的key在类中没有对应的属性,就会调用setValue:forUndefinedKey:函数去处理这个情况。如果没有实现setValue:forUndefinedKey:函数时,会抛出NSUndefinedKeyException,程序崩溃

所以应该根据具体的项目需求,来实现setValue:forUndefinedKey:函数

如果父类实现了setValue:forUndefinedKey:,子类不必再次实现这个方法了。


在 Swift 中想要兼容 KVC,需要该类继承 NSObject。下面是 KVC 中常用的几个方法:


根据传入的 key,设置 value:

func setValue(_ value:Any?, forKey key:String)


根据传入的 keyPath,设置 value,keyPath 即键路径可利用 . 遍历至键:

func setValue(_ value:Any?, forKeyPath keyPath:String)


根据传入的字典,设值,如果字典中有对象不存在的属性,则会抛出异常:

func setValuesForKeys(_ keyedValues: [String :Any])


当为不存在的某个键设值时,默认调用该方法抛出 NSUndefinedKeyException 异常,子类可重写该方法:

func setValue(_ value:Any?, forUndefinedKey key:String)


设置特定键的值为 nil 时,默认调用该方法抛出 NSInvalidArgumentException,子类可重写该方法(注:官方称该方法针对标量值(scalar value),例如整型和浮点型)对于其他类型没有提到,在 Demo 中有详细的列出是否支持):

func setNilValueForKey(_ key:String)


返回传入指定键的对应值:

func value(forKey key: String) -> Any?


返回传入指定键路径的对应值:

func value(forKeyPath keyPath: String) -> Any?


返回传入未定义的键路径的对应值:

func value(forUndefinedKey key: String) -> Any?


=====================kvc字典转模型===============================================


*****第一层模型

import UIKit


class LYBHomeLunboModel: NSObject {

    @objc var name:String?="11" //swift4.0中要加上@objc,否则在外面调用不到

    

    @objc var age:NSNumber?

    @objc var phones:[LYBPhonesModel]?

 init(dict:[String:Any]) {

        super.init()

         setValuesForKeys(dict)

    }

    override func setValue(_ value:Any?, forKey key:String) {

        

        if key=="phones"{

            let temp = value as! [AnyObject]

                var resultArray = [LYBPhonesModel]()

                for dict in temp {

                resultArray.append(LYBPhonesModel(dict: dictas! [String :AnyObject]))

            

        }

            phones = resultArray

            return

                    }

        super.setValue(value, forKey: key)

    }

    override func setValue(_ value:Any?, forUndefinedKey key:String) {

    }

    

   

}

*****************第二层模型*********

import UIKit


class LYBPhonesModel: NSObject {

   @objc var name:String?

   @objc var number:String?

    

    init(dict: [String:AnyObject]) {

        super.init()

        

        setValuesForKeys(dict)

    }

    

    override func setValue(_ value:Any?, forKey key:String) {

        super.setValue(value, forKey: key)

    }

    override func setValue(_ value:Any?, forUndefinedKey key:String) {

        

    }

}




*****************

//kvc

    func swiftkvc(){

     

 //1.这是一个JSON字符串

        let jsonStr ="[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 1,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"

        

        //2.吧JSON字符串变成data数据

        if let jsonData = jsonStr.data(using:String.Encoding.utf8, allowLossyConversion:false) {

          

    //***************系统的序列化成json*************

            do{

//                //3.吧jsondata转换成JSON对象(即字典,或字典数组)

                if  let dictArr:[[String:Any]]=(tryJSONSerialization.jsonObject(with:jsonData , options: .allowFragments)as?[[String :Any]])  {

                    let dict:[String:Any]=dictArr[1]

                    let model:LYBHomeLunboModel=LYBHomeLunboModel(dict:dict)

                    

                    print("\(model.phones![0].name!)")

                }

            }

        catch {


            }

        

    }


==================


2017-12-05 20:42:51 longshihua 阅读数 1354
  • iOS从初级到精通就业 Objective-C

    C语言基础入门,面向初级,毫无编程经验的学生群体,通过学习能够掌握C语言的基本语法,编程的基本概念,培养自己的编程思维!OC语言详解,掌握OC核心内容(面向对象、内存管理,类的扩展,KVC和KVO,闭包和常见的Foundation类库的使用方法等,为后续学习iOS SDK打好基础。

    10086 人正在学习 去看看 栾斌

KVC和KVO是我们开发中常用的功能,现在来看一下在Swift4中的变化

KVC

在Swift4的时候,Struct也支持KVC,我们不在使用setValue: forKeypath的方式,而是使用新的语法特性,下面看一下例子,参考这里

struct Person {
    var name: String
}

struct Book {
    var title: String
    var authors: [Person]
    var primaryAuthor: Person {
        return authors.first!
    }
}

let abelson = Person(name: "Haeold Abelson")
let sussman = Person(name: "Garald Jay Sussman")
var book = Book(title: "tructure and Interpretation of Computer Programs", authors: [abelson, sussman])

//get
//1 keyPath以\开始,然后开始组合结构体和属性
let title = book[keyPath: \Book.title]
print(title) //tructure and Interpretation of Computer Programs

//2 keypath可以进入多层深入搜索查找,也可以对计算属性进行操作
let name = book[keyPath: \Book.primaryAuthor.name]
print(name) //Haeold Abelson

//set
book[keyPath: \Book.title] = "KVC"
print(book[keyPath: \Book.title]) // KVC

对象的路径操作

//先获取一个路径
let authorKeyPath = \Book.primaryAuthor
//拼接子路径
let nameKeyPath = authorKeyPath.appending(path: \.name)
let newName = book[keyPath: nameKeyPath]
print(newName) //Haeold Abelson

KVO

@objcMembers class Food: NSObject {
    dynamic var string: String
    override init() {
        string = "hotdog"
        super.init()
    }
}

let food = Food()
let observation = food.observe(\.string) { (foo, changed) in
     print("new food string: \(foo.string)")
}
food.string = "not hotdog" // new food string: not hotdog
上面代码很简单,创建了一个Food类,拥有一个string属性,但是需要注意几件事情:

1)用KVO依然需要是NSObject类或子类,Swift4中swift类不再自动被推测为继承于NSObject,所以当我们在编写swift的代码时,需要为类添加@objcMembers关键字

2)注意到属性string,我们使用了dynamic关键字,主要是告诉观察者在值发生改变之后触发闭包,如果没有该关键字,那么无法观察到值的改变

3)使用新语法特性\.string监听属性变化

4)最开心的事就是我们不在需要手动去除观察者,以前都需要在deinit()中去除观察者


当然,我们也可以为属性添加@objc,那么类就不在需要@objcMembers关键


class Child: NSObject {
    let name: String
    // KVO-enabled properties must be @objc dynamic
    @objc dynamic var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
        super.init()
    }

    func celebrateBirthday() {
        age += 1
    }
}
使用方法是一样,接下来使用带有options参数的方法:

//Set up KVO
let mia = Child(name: "Mia", age: 5)
let observation = mia.observe(\.age, options: [.initial, .old]) { (child, change) in
    if let oldValue = change.oldValue {
        print("\(child.name)’s age changed from \(oldValue) to \(child.age)")
        //Mia’s age changed from 5 to 6
    } else {
        print("\(child.name)’s age is now \(child.age)")
        //Mia’s age is now 5
      }
    }
//Trigger KVO (see output in the console)
mia.celebrateBirthday()

//Deiniting or invalidating the observation token ends the observation
observation.invalidate()

//This doesn't trigger the KVO handler anymore
mia.celebrateBirthday()

options参数,我们设置了两个值,.initial,.old,表示获取最开始的值,和变化前的值。KVO的options一共有4种:

public struct NSKeyValueObservingOptions : OptionSet {
    public init(rawValue: UInt)
    public static var new: NSKeyValueObservingOptions { get } //变化前的值
    public static var old: NSKeyValueObservingOptions { get } //变化后的值
    public static var initial: NSKeyValueObservingOptions { get } // 初始值
    public static var prior: NSKeyValueObservingOptions { get } // notification变化前後的标准
}


监听WebKit加载进度

class ViewController: UIViewController {
    var webView: WKWebView!
    var urlPath: String = "https://www.baidu.com/"
    var progressView: UIProgressView!
    var observer: NSKeyValueObservation! 

    override func viewDidLoad() {
        super.viewDidLoad()
          setupWebView()
    }

    func setupWebView() {
        webView = WKWebView(frame: view.frame)
        view.addSubview(webView)

        progressView = UIProgressView(frame: CGRect(x: 0, y: 43, width: view.bounds.width, height: 1.0))
        navigationController?.navigationBar.addSubview(progressView)

        observer = webView.observe(\.estimatedProgress, options: .new) { [weak self] (_, changed) in
            if let new = changed.newValue {
                self?.changeProgress(Float(new))
            }
        }
        if let url = URL(string: urlPath) { webView.load(URLRequest(url: url)) }
    }

    func changeProgress(_ progress: Float) {
        progressView.isHidden = progress == 1
        progressView.setProgress(progress, animated: true)
    }
}



自定义观察者

1、声明一个泛型的观察者类,并拥有一个嵌套回调类

//1、 自己封装观察者
public class Observable<Type> {
    //2、 内嵌回调类
    fileprivate class Callback {
        fileprivate weak var observer: AnyObject?
        fileprivate let options: [ObservableOptions]
        fileprivate let closure: (Type, ObservableOptions) -> Void

        init(observer: AnyObject, options: [ObservableOptions], closure: @escaping  (Type, ObservableOptions) -> Void) {
            self.observer = observer
            self.options = options
            self.closure = closure
        }
    }
}

Callback中关联观察者(observer)、可选项(options)、回调闭包(closure),并且观察者是弱引用可以为任意对象。

2、声明可选项结构体

public struct ObservableOptions: OptionSet {
    public static let initial = ObservableOptions(rawValue: 1 << 0)
    public static let old = ObservableOptions(rawValue: 1 << 1)
    public static let new = ObservableOptions(rawValue: 1 << 2)

    public var rawValue: Int
    public init(rawValue: Int) {
        self.rawValue = rawValue
    }
}

可以看到非常简单,遵守OptionSet协议,并拥有initial、old、new3个值,跟系统本身KVO中的NSKeyValueObservableOptions有点类似。

3、在Observable中声明泛型的value属性

// 无论什么时候值发生改变,发送通知
public var value: Type {
    didSet {
        // notification
    }
}

public init(_ value: Type) {
    self.value = value
}

4、在Observable中增加添加观察者、去除观察者等方法

 // 回调
    private var callbacks: [Callback] = []

    // 添加观察者
    public func addObserver(_ observer: AnyObject,
                            removeIfExists: Bool = true,
                            options: [ObservableOptions] = [.new],
                            closure: @escaping (Type, ObservableOptions) -> Void) {
        if removeIfExists {
            removeObserver(observer)
        }
        let callback = Callback(observer: observer, options: options, closure: closure)
        callbacks.append(callback)

        if options.contains(.initial) {
            closure(value, .initial)
        }
    }

    // 去除观察者
    public func removeObserver(_ observer: AnyObject) {
        callbacks = callbacks.filter { $0.observer !== observer }
    }

    // 去除观察者为nil的回调
    private func removeNilObserverCallbacks() {
        callbacks = callbacks.filter { $0.observer != nil }
    }

5、监听属性改变,添加回调方法

public var value: Type {
     didSet {
        // 去除为nil的通知,防止当观察者被释放掉之后然后使用所造成的crash
        removeNilObserverCallbacks()
        // 回调旧值和新值
        notifyCallbacks(value: oldValue, options: .old)
        notifyCallbacks(value: value, options: .new)
    }
}

private func notifyCallbacks(value: Type, options: ObservableOptions) {
    let callbacksToNotify = callbacks.filter { $0.options.contains(options) }
    callbacksToNotify.forEach { $0.closure(value, options) }
}

到这里我们自己封装的观察者已经完成,简单使用一下

1、声明User和Observer类

public class User {
    public let name: Observable<String> 
    public init(name: String) {
        self.name = Observable(name)
    }
}

public class Observer {}

2、简单使用

let myUser = User(name: "Jack")  // 可观察对象
var observer: Observer? = Observer() // 观察者,观察者可以是NSObject的任意实例或者任意类
// 对name进行观察
myUser.name.addObserver(observer!, options: [.initial, .new]) { (name, change) in
    print("user is name is: \(name)")
}
// 设置value来更新其值
myUser.name.value = "hello jack"
// 去除观察者,那么后面其值再次改变将不受影响,因为内部的实现告诉我们其相关回调已经被去除
observer = nil
// 监听无效
myUser.name.value = "poor jack"


参考:

Key Value Observation in iOS 11

Smart KeyPaths: Better Key-Value Coding for Swift

What's new in swift4




Swift中KVC和KVO的使用

阅读数 12086

iOS KVC解析Json数据

阅读数 396

没有更多推荐了,返回首页