setValue forKey和setObject forKey的区别

本文详细解析了NSMutableDictionary中setValue:forKey:与setObject:forKey:的区别,包括nil值处理方式的不同以及对key类型的限制。同时介绍了这两种方法在不同场景下的应用。

在使用NSMutableDictionary的时候经常会使用setValue forKey与setObject forKey,他们经常是可以交互使用的,代码中经常每一种的使用都有。

1,先看看setValue: forKey:的定义

@interface NSMutableDictionary(NSKeyValueCoding)

/* Send -setObject:forKey: to the receiver, unless the value is nil, in which case send -removeObject:forKey:.

*/

- (void)setValue:(id)value forKey:(NSString *)key;

@end

扩展NSMutableDictionary的一个类别,上面注释说的很清楚,发送setObject:forKey 给接收者,也就是调用setObject:forKey方法

除非value为nil的时候,调用方法removeObject:forKey


2,看看setObject:forKey:的定义

@interface NSMutableDictionary :NSDictionary

- (void)removeObjectForKey:(id)aKey;

- (void)setObject:(id)anObject forKey:(id <NSCopying>)aKey;

@end

注意:setObject:forKey:中Key的对象是一个id类型,并不是NSString,只不过我们经常使用NSString而已。


现在总结他们2者的区别就是:

1, setObject:forkey:中value是不能够为nil的,不然会报错。

setValue:forKey:中value能够为nil,但是当value为nil的时候,会自动调用removeObject:forKey方法

2, setValue:forKey:中key的参数只能够是NSString类型,而setObject:forKey:的可以是任何类型


注意:setObject:forKey:对象不能存放nil要与下面的这种情况区分:

1, [imageDictionarysetObject:[NSNullnullforKey:indexNumber];

[NSNull null]表示的是一个空对象,并不是nil,注意这点


2, setObject:forKey:中Key是NSNumber对象的时候,如下:

    [imageDictionarysetObject:obj forKey:[NSNumber numberWithInt:10]];


注意:

上面说的区别是针对调用者是dictionary而言的。

setObject:forKey:方法NSMutabledictionary特有的,

setValue:forKey:方法是KVC(键-值编码)的主要方法。


当 setValue:forKey:方法调用者是对象的时候:

setValue:forKey:方法是在NSObject对象中创建的,也就是说所有的oc对象都有这个方法,所以可以用于任何类。

比如使用:

SomeClass *someObj = [[SomeClass alloc] init];

[someObj setValue:self forKey:@"delegate"];

表示的意思是:对象someObj设置他的delegate属性的值为当前类,当然调用此方法的对象必须要有delegate属性才能设置,不然调用了也没效果


import UIKit import CoreLocation import CoreBluetooth protocol PositionProviderDelegate: AnyObject { func didUpdate(position: Position) } class PositionProvider: NSObject, CLLocationManagerDelegate, CBCentralManagerDelegate { weak var delegate: PositionProviderDelegate? let userDefaults: UserDefaults var locationManager: CLLocationManager var lastLocation: CLLocation? var deviceId: String var interval: Double var distance: Double var angle: Double var startStopTimer: Timer? var beaconRegion: CLBeaconRegion? var centralManager: CBCentralManager? var isScanning = false var scanTimer: Timer? var keepAlive = false var pendingStart = false override init() { userDefaults = UserDefaults.standard deviceId = userDefaults.string(forKey: "device_id_preference")! interval = userDefaults.double(forKey: "frequency_preference") distance = userDefaults.double(forKey: "distance_preference") angle = userDefaults.double(forKey: "angle_preference") locationManager = CLLocationManager() super.init() locationManager.delegate = self locationManager.pausesLocationUpdatesAutomatically = false switch userDefaults.string(forKey: "accuracy_preference") ?? "medium" { case "high": locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation case "low": locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters default: locationManager.desiredAccuracy = kCLLocationAccuracyBest } if #available(iOS 9.0, *) { locationManager.allowsBackgroundLocationUpdates = true } } func startUpdates(distance: Double) { locationManager.distanceFilter = distance locationManager.activityType = .other if (distance > 10.0) { locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters } else { switch userDefaults.string(forKey: "accuracy_preference") ?? "medium" { case "high": locationManager.desiredAccuracy = kCLLocationAccuracyBest case "low": locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters default: locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters } } if HXGPSController.shared.isSingleRealTimeLocation == false { keepAlive = distance > 10.0 } interval = userDefaults.double(forKey: "frequency_preference") if #available(iOS 14.0, *) { // iOS 14 及以上版本 switch locationManager.authorizationStatus { case .authorizedAlways: locationManager.startUpdatingLocation() startScanCycle() default: pendingStart = true locationManager.requestAlwaysAuthorization() } } else { // iOS 13 及以下版本 switch CLLocationManager.authorizationStatus() { case .authorizedAlways: locationManager.startUpdatingLocation() startScanCycle() default: pendingStart = true locationManager.requestAlwaysAuthorization() } } } func stopUpdates(isClearLastLocation: Bool = true) { locationManager.stopUpdatingLocation() if isClearLastLocation { lastLocation = nil } stopControlTimer() // 停止扫描循环 scanTimer?.invalidate() scanTimer = nil stopBeaconScan() } // 启动蓝牙扫描定时器 func startScanCycle() { scanTimer?.invalidate() if userDefaults.string(forKey: "bluetooth_open_status") == "0" { return } // 开始扫描 startBeaconScan() // 启动定时器,10秒后停止扫描 let gap = Double(userDefaults.string(forKey:"bluetooth_interval") ?? "10") ?? 10.0 scanTimer = Timer.scheduledTimer(withTimeInterval: gap + 3.0, repeats: true) { [weak self] timer in guard let self = self else { return } if self.isScanning { // 当前在扫描状态,停止扫描 self.stopBeaconScan() // 5秒后重新开始扫描 let bleDelay = Double(userDefaults.string(forKey:"bluetooth_duration") ?? "5") ?? 5.0 DispatchQueue.main.asyncAfter(deadline: .now() + bleDelay + 3.0) { self.startBeaconScan() } } else { // 当前在停止状态,开始扫描 self.startBeaconScan() } } } // 启动蓝牙扫描 func startBeaconScan() { if !isScanning { let beaconConstraint = CLBeaconIdentityConstraint(uuid: UUID(uuidString: "FDA50693-A4E2-4FB1-AFCF-C6EB07647825")!) locationManager.startRangingBeacons(satisfying: beaconConstraint) isScanning = true } } // 停止蓝牙扫描 func stopBeaconScan() { if isScanning { let beaconConstraint = CLBeaconIdentityConstraint(uuid: UUID(uuidString: "FDA50693-A4E2-4FB1-AFCF-C6EB07647825")!) locationManager.stopRangingBeacons(satisfying: beaconConstraint) isScanning = false // 更新 UserDefaults 中的 ble 值 let bleValues = HXGPSController.shared.discoveredDevices.values.map { String(describing: $0) }.joined(separator: ",") userDefaults.set(bleValues, forKey: "ble") userDefaults.set(bleValues, forKey: "ble_for_check_in") } } // 启动定位启停控制定时器 func startControlTimer() { if (startStopTimer != nil) { startStopTimer?.invalidate(); } startStopTimer = Timer.scheduledTimer(timeInterval: TimeInterval(interval >= 0 && interval - 30 >= 30 ? interval - 30 : 30), target: self, selector: #selector(controlUpdatingLocation), userInfo: nil, repeats: true) } // 停止定位启停控制定时器 func stopControlTimer() { startStopTimer?.invalidate(); startStopTimer = nil } // 启停定时器回调 @objc func controlUpdatingLocation() { self.locationManager.stopUpdatingLocation() self.locationManager.distanceFilter = 0.0 self.locationManager.startUpdatingLocation() } // 撤回始终后台定位权限 func revokeAlwaysAuthorization() { // 撤回定位授权,引导用户进入设置页 if let url = URL(string: UIApplication.openSettingsURLString) { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } } } func getBatteryStatus() -> BatteryStatus { let device = UIDevice.current // NSLog("deviceinfo:\(device)") // NSLog("deviceBatteryState:\(device.batteryState)") // NSLog("deviceBatteryLevel:\(device.batteryLevel)") if device.batteryState != .unknown { return BatteryStatus( level: device.batteryLevel * 100, charging: device.batteryState == .charging || device.batteryState == .full ) } else { return BatteryStatus(level: 0, charging: true) } } func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { switch status { case .authorizedAlways, .authorizedWhenInUse: // 首次启动应用时不执行 if !HXGPSController.shared.isFirstStart { locationManager.requestAlwaysAuthorization() } if pendingStart { pendingStart = false locationManager.startUpdatingLocation() } default: // 首次启动应用时不执行 if !HXGPSController.shared.isFirstStart { locationManager.requestAlwaysAuthorization() } break } } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { // NSLog("locationinfo:\(locations)") // 设备未激活或者班次外不上传服务器 // let deActived = userDefaults.string(forKey: "device_is_actived") ?? "0" let deActived = "1" let stealthMode = userDefaults.string(forKey: "stealth_mode") ?? nil let classBeginEnd = userDefaults.string(forKey: "class_begin_end") ?? nil let chargeEvent = userDefaults.string(forKey: "charge_event") ?? nil let isLocation = userDefaults.string(forKey: "is_location"); // 处理flutter中获取实时位置 if HXGPSController.shared.isSingleRealTimeLocation { var tmpRealTimeLocation: CLLocation? if let realTimeLocation = locations.last { userDefaults.setValue("\(realTimeLocation.coordinate.latitude)&\(realTimeLocation.coordinate.longitude)", forKey: "camera_lat_lng") HXGPSController.shared.isSingleRealTimeLocation = false tmpRealTimeLocation = realTimeLocation } if (isLocation == "0" || keepAlive) { DispatchQueue.main.async { if let updateLastLocation = tmpRealTimeLocation { self.lastLocation = CLLocation( coordinate: updateLastLocation.coordinate, altitude: updateLastLocation.altitude, horizontalAccuracy: updateLastLocation.horizontalAccuracy, verticalAccuracy: updateLastLocation.verticalAccuracy, course: updateLastLocation.course, speed: updateLastLocation.speed, timestamp: Date() ) } self.locationManager.stopMonitoringSignificantLocationChanges() self.stopUpdates(isClearLastLocation: false) self.startUpdates(distance: 5000.0) } } } // 判断是否需要上报位置 if (deActived == "0" || isLocation == "0" || keepAlive || (stealthMode == "1" && classBeginEnd == nil && chargeEvent == nil)) { return } // 处理定位以及相关数据 if let location = locations.last { if lastLocation == nil || location.timestamp.timeIntervalSince(lastLocation!.timestamp) >= interval || (distance > 0 && DistanceCalculator.distance(fromLat: location.coordinate.latitude, fromLon: location.coordinate.longitude, toLat: lastLocation!.coordinate.latitude, toLon: lastLocation!.coordinate.longitude) >= distance) || (angle > 0 && fabs(location.course - lastLocation!.course) >= angle) { // 添加定位数据更新运行log if let locationUploadLog = userDefaults.string(forKey: "location_fresh_log") { userDefaults.setValue("\(locationUploadLog)\(HXGPSController.shared.getHHmmss())+", forKey: "location_fresh_log") } else { userDefaults.setValue("\(HXGPSController.shared.getHHmmss())+", forKey: "location_fresh_log") } let position = Position(managedObjectContext: DatabaseHelper().managedObjectContext) position.deviceId = deviceId let batteryStatus = getBatteryStatus() position.batt = Int(batteryStatus.level) as NSNumber position.charge = batteryStatus.charging as Bool position.gpsNumber = 12 as NSNumber position.gpsIntensity = 99 as NSNumber position.isHeartbeat = "1" as NSString position.valid = 1 as NSNumber let bleStr = userDefaults.string(forKey: "ble") ?? "" position.ble = bleStr as NSString let mainBundle = Bundle.main if let appVersion = mainBundle.infoDictionary?["CFBundleShortVersionString"] as? String, let _ = mainBundle.infoDictionary?["CFBundleVersion"] as? String { position.buildVersion = appVersion as NSString userDefaults.setValue(appVersion, forKey: "build_version") } else { position.buildVersion = "1.0" as NSString userDefaults.setValue("1.0", forKey: "build_version") } // 保存心跳数据 if (lastLocation == nil) { userDefaults.setValue(String(describing: Int(Date().timeIntervalSince1970)), forKey: "gps_time") } else { userDefaults.setValue(String(describing: Int(location.timestamp.timeIntervalSince1970)), forKey: "gps_time") } let tmpBatt = Int(batteryStatus.level) let tmpCharge = Bool(batteryStatus.charging) userDefaults.setValue(tmpBatt, forKey: "batt") userDefaults.setValue(tmpCharge ? 1 : 0, forKey: "charge") userDefaults.setValue(location.horizontalAccuracy, forKey: "location_accuracy") userDefaults.setValue(location.course, forKey: "location_bearing") userDefaults.setValue(location.altitude, forKey: "location_altitude") if let step = userDefaults.string(forKey: "step_number") { position.step = step as NSString } if let state = userDefaults.string(forKey: "cstate") { position.cstate = state as NSString } if let isMove = userDefaults.string(forKey: "current_mode") { position.isMove = "\(isMove == "1" ? 1 : 0)" as NSString } if let isStealthMode = userDefaults.string(forKey: "stealth_mode") { position.isVisible = "\(isStealthMode)" as NSString } position.isCharge = "\(tmpCharge ? 1 : 0)" as NSString if let isTracking = userDefaults.string(forKey: "is_location") { position.isTracking = "\(isTracking)" as NSString } position.isReturn = "" as NSString // 保存当前定位速度 userDefaults.setValue(location.speed, forKey: "location_speed") let tmpLocation = CLLocation( coordinate: location.coordinate, altitude: location.altitude, horizontalAccuracy: location.horizontalAccuracy, verticalAccuracy: location.verticalAccuracy, course: location.course, speed: location.speed, timestamp: location.timestamp ) position.setLocation(tmpLocation) if (lastLocation == nil && Date().timeIntervalSince(location.timestamp) >= 5) { lastLocation = CLLocation( coordinate: location.coordinate, altitude: location.altitude, horizontalAccuracy: location.horizontalAccuracy, verticalAccuracy: location.verticalAccuracy, course: location.course, speed: location.speed, timestamp: Date() ) } else { lastLocation = location } delegate?.didUpdate(position: position) } } } func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { } // CLBeacon逻辑 func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) { } func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) { if let beaconRegion = region as? CLBeaconRegion { } } func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) { if let beaconRegion = region as? CLBeaconRegion { } } func locationManager(_ manager: CLLocationManager, didRangeBeacons beacons: [CLBeacon], in region: CLBeaconRegion) { if beacons.isEmpty { return } // 循环处理所有检测到的 beacon for beacon in beacons { // 检查 major minor 是否符合条件 if beacon.major != nil && beacon.minor != nil && beacon.major.intValue != 1 && beacon.minor.intValue != 2 { // 将 major 拆分成高8位低8位 let majorValue = UInt16(beacon.major.intValue) let majorHigh = (majorValue >> 8) & 0xFF let majorLow = majorValue & 0xFF // 将 minor 拆分成高8位低8位 let minorValue = UInt16(beacon.minor.intValue) let minorHigh = (minorValue >> 8) & 0xFF let minorLow = minorValue & 0xFF // 用冒号拼接 let majorMinorStr = String(format: "%02X:%02X:%02X:%02X", majorHigh, majorLow, minorHigh, minorLow) // 拼接mac前缀 let bleIdentifier = "58:06:\(majorMinorStr)" var deviceInfo = [String: Any]() deviceInfo["timestamp"] = Int(Date().timeIntervalSince1970) * 1000 deviceInfo["rssi"] = 100 deviceInfo["mac"] = bleIdentifier deviceInfo["battery"] = 100 if let macAddress = deviceInfo["mac"] as? String { HXGPSController.shared.discoveredDevices[macAddress] = "\(macAddress);\(deviceInfo["rssi"] ?? 0);\(deviceInfo["timestamp"] ?? Int(Date().timeIntervalSince1970) * 1000);\(deviceInfo["battery"] ?? 0)" } } } } // 蓝牙状态更新回调 func centralManagerDidUpdateState(_ central: CBCentralManager) { if central.state == .poweredOn { startScanCycle() } else { // 停止扫描循环 scanTimer?.invalidate() scanTimer = nil HXGPSController.shared.clearBleMap() userDefaults.removeObject(forKey: "ble") userDefaults.removeObject(forKey: "ble_for_check_in") stopBeaconScan() } } }
最新发布
10-12
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值