简体   繁体   English

EKEvent的镜像不显示数据

[英]Mirror of EKEvent does not show the data

For my EVReflection library I came to a case where a Mirror for an EKEvent did not return any information. 对于我的EVReflection库,我遇到了EKEvent的镜像未返回任何信息的情况。 Even when going to the complete basics a Mirror did not return anything. 即使使用完整的基础知识,Mirror也不会返回任何内容。 When you set a breakpoint after the Mirror line, you will see that there is nothing in the Mirror object. 在Mirror行之后设置断点时,您将看到Mirror对象中没有任何内容。 Is this a bug or am I missing something? 这是错误还是我错过了什么?

update: I extended the demo for getting the properties using the old Objective C objc_property_t and property_getName functions. 更新:我扩展了使用旧的Objective C objc_property_t和property_getName函数获取属性的演示。 It will return a list of properties. 它将返回属性列表。 But then it will crash when you get the .value(forKey: 但是,当您获得.value(forKey:

#if os(tvOS)
    // Eventkit is not supported on tvOS
#else


import XCTest
import Foundation
import EventKit


let store = EKEventStore()

class EVReflectionEventKitTests: XCTestCase {
    func testEventKit() {

        let exp = expectation(description: "eventStore")

        store.requestAccess(to: .event, completion: { (granted, error) in
            let event = EKEvent(eventStore: store)
            event.startDate = Date().addingTimeInterval(10000)
            event.title = "title"
            event.location = "here"
            event.endDate = Date().addingTimeInterval(20000)
            event.notes = "notes"

            event.calendar = store.defaultCalendarForNewEvents
            event.addAlarm(EKAlarm(absoluteDate: Date().addingTimeInterval(10000)))

            //WARNING: You will get events in your agenda! Disable next line if you don't want that
            //try? store.save(event, span: EKSpan.thisEvent, commit: true)

            let m = Mirror(reflecting: event)
            print("mirror children = \(m.children.count)")

            let oc = self.properties(event)
            print(oc)

            for p in oc {
                var value: Any? = nil
                value = event.value(forKey: p)
                print("\(p) = \(String(describing: value))")
            }

            exp.fulfill()
        })

        waitForExpectations(timeout: 10) { error in
            XCTAssertNil(error, "\(error?.localizedDescription ?? "")")
        }
    }

    func properties(_ classToInspect: NSObject) -> [String] {
        var count = UInt32()
        let classToInspect = NSURL.self
        let properties = class_copyPropertyList(classToInspect, &count)
        var propertyNames = [String]()
        let intCount = Int(count)
        for i in 0 ..< intCount {
            let property : objc_property_t = properties![i]!
            guard let propertyName = NSString(utf8String: property_getName(property)) as String? else {
                debugPrint("Couldn't unwrap property name for \(property)")
                break
            }
            propertyNames.append(propertyName)
        }

        free(properties)
        return propertyNames
    }
}

#endif

This appears to be intended behavior: 这似乎是预期的行为:

If it were non-zero, it'd be dumping ivars, not properties. 如果它不为零,则将转储ivars,而不是属性。 A property's getter attribute can make it so there's no method matching the property name, and its backing ivar can also be named differently from the property, if there even is one: both of these could cause -valueForKey: to fail out. 属性的getter属性可以使属性匹配,因此没有方法可以匹配属性名称,并且即使存在属性,其后缀ivar也可以与属性不同地命名:两者都可能导致-valueForKey:失败。 Most classes don't disable direct ivar access via key-value coding, so you might want to try dumping the ivars instead and accessing those. 大多数类都不会通过键值编码禁用直接ivar访问,因此您可能想尝试转储ivars并访问它们。

You might also want to note that some classes lie about their layout, like NSURL , which was explicitly blacklisted from reflection even when reflection is enabled for Obj-C classes! 您可能还需要注意,有些类位于它们的布局上,例如NSURL ,即使为Obj-C类启用了反射NSURL已从反射中明确列入黑名单

ETA: Here's an example of dumping the ivars. 预计到达时间这是一个丢弃ivars的示例。 It's not terribly useful in this case, unfortunately. 不幸的是,在这种情况下它并不是非常有用。

The code to generate a dictionary based on the ivars: 基于ivars生成字典的代码:

func makeDictionary(describing object: AnyObject) -> Dictionary<String, Any> {
    var dict = [String: Any]()
    guard let cls = object_getClass(object) else {
        print("object has no class:", object)
        return dict
    }

    var ivarCount: UInt32 = 0
    guard let ivarsPtr: UnsafeMutablePointer<Ivar?> = class_copyIvarList(cls, &ivarCount), ivarCount > 0
    else {
        print("object has no ivars, or failed to get any:", object)
        return dict
    }

    let ivars = UnsafeBufferPointer(start: ivarsPtr, count: numericCast(ivarCount))
    for ivar in ivars {
        guard let int8Ptr = ivar_getName(ivar) else {
            print("failed to get name for ivar:", ivar as Any)
            continue
        }

        let name = String(cString: int8Ptr)
        let value = object_getIvar(object, ivar)
        print("value for \(name):", value as Any)
        dict[name] = value ?? object.value(forKey: name) ?? NSNull()
    }
    return dict
}

And the output for an event created as described in your question (manually hard-wrapped for ease of review): 以及根据您的问题所述创建的事件的输出(为便于查看,进行了手动包装):

dictionary version based on direct ivar access: 
["isYearlessLeapMonthBirthday": 0
, "_birthdayPersonID": 0
, "_sliceDate": <null>
, "nameForBirthday": <null>
, "_futureLocalUidForSliceChild": <null>
, "isYearlessBirthday": 0
, "_cachedDuration": <null>
, "_cachedStartOfDayForStartDate": <null>
, "_isPhantom": 0
, "lunarCalendarString": <null>
, "_cachedIsMultiDayTimedEvent": <null>
, "_cachedTimeValuesCalendar": <null>
, "birthdayTitle": <null>
, "_cachedStartOfDayForEndDate": <null>
, "_cachedDaysSpanned": <null>
, "_cachedJunkStatus": 0
, "sliceParentID": <null>
, "participantsStatus": 0
]

I made a SERIALIZER.SWIFT class, like the example in github you provided: 我制作了SERIALIZER.SWIFT类,就像您提供的github中的示例一样:

import Foundation
import EventKit
import EVReflection

class serializedEvent: EVObject {
var myEvent: EKEvent = EKEvent(eventStore: globalStore)
//globalstore is an EKEventstore global object

then in one of my controllers i do (i just have a full event named: event created with alarms of type EKalarms and recurrency Rule of EKRecurrencyRule type): 然后在我的一个控制器中做(我只有一个完整的事件,名为:用EKalarms类型的警报和EKRecurrencyRule类型的重复规则创建的事件):

print("event before serialization :")
  print(event)
  let serEvent: serializedEvent = serializedEvent()
  serEvent.myEvent=event

  let jsonEvent = serEvent.toJsonString()

  let newEvent = serializedEvent(json: jsonEvent).myEvent
  print("newEvent: =")
  print(newEvent)

This is a real example output : 这是一个真实的示例输出:

event before serialization :
   EKEvent <0x174130040>
   {
     EKEvent <0x174130040>
    {    title =        Matrimonio; 
    location =  ; 
        calendar =  EKCalendar <0x1740b4d60> {title = Calendario; type = CalDAV; allowsModify = YES; color = #029ae4;}; 
       alarms =         (
      "EKAlarm <0x1740d9d00> {triggerInterval = -54000.000000}",
      "EKAlarm <0x1740d8950> {triggerInterval = 0.000000}"
  ); 
     URL =          (null); 
 lastModified = 2016-06-06 15:15:17 +0000; 
 startTimeZone =    (null); 
 startTimeZone =    (null) 
 }; 
 location =     ; 
 structuredLocation =   (null); 
 startDate =    2009-06-06 22:00:00 +0000; 
 endDate =      2009-06-07 21:59:59 +0000; 
 allDay =       1; 
 floating =     1; 
 recurrence =   EKRecurrenceRule <0x1702b2c00> RRULE FREQ=YEARLY;INTERVAL=1;BYMONTH=6;BYMONTHDAY=7; 
 attendees =    (null); 
 travelTime =   (null); 
 startLocation =    (null);
   };
  2017-04-28 21:23:25.226855 InstaEvent Share[19166:5967595] ERROR: 
   Unexpected type while converting value for JsonSerialization: 
  EKEvent <0x174130040>
  {
 EKEvent <0x174130040>
  {  title =        Matrimonio; 
    location =  ; 
     calendar =     EKCalendar <0x1740b4d60> {title = Calendario; type 
  = CalDAV; allowsModify = YES; color = #029ae4;}; 
     alarms =       (
    "EKAlarm <0x1740d9d00> {triggerInterval = -54000.000000}",
    "EKAlarm <0x1740d8950> {triggerInterval = 0.000000}"
  ); 
      URL =             (null); 
 lastModified = 2016-06-06 15:15:17 +0000; 
 startTimeZone =    (null); 
 startTimeZone =    (null) 
 }; 
 location =     ; 
 structuredLocation =   (null); 
 startDate =    2009-06-06 22:00:00 +0000; 
 endDate =      2009-06-07 21:59:59 +0000; 
 allDay =       1; 
 floating =     1; 
     recurrence =   EKRecurrenceRule <0x1702b2c00> RRULE 
     FREQ=YEARLY;INTERVAL=1;BYMONTH=6;BYMONTHDAY=7; 
 attendees =    (null); 
 travelTime =   (null); 
 startLocation =    (null);
  };

 newEvent: =
  EKEvent <0x174130040>
 {
     EKEvent <0x174130040>
 {   title =        Matrimonio; 
       location =   ; 
       calendar =   EKCalendar <0x1740b4d60> {title = Calendario; type 
     = CalDAV; allowsModify = YES; color = #029ae4;}; 
       alarms =         (
      "EKAlarm <0x1740d9d00> {triggerInterval = -54000.000000}",
      "EKAlarm <0x1740d8950> {triggerInterval = 0.000000}"
  ); 
     URL =          (null); 
      lastModified = 2016-06-06 15:15:17 +0000; 
     startTimeZone =    (null); 
     startTimeZone =    (null) 
  }; 
 location =     ; 
 structuredLocation =   (null); 
 startDate =    2009-06-06 22:00:00 +0000; 
 endDate =      2009-06-07 21:59:59 +0000; 
 allDay =       1; 
 floating =     1; 
 recurrence =   EKRecurrenceRule <0x1702b2c00> RRULE 
  FREQ=YEARLY;INTERVAL=1;BYMONTH=6;BYMONTHDAY=7; 
 attendees =    (null); 
 travelTime =   (null); 
 startLocation =    (null);
  };

but when we ask if it has alarms (and it should have, looking at output) 但是当我们问它是否有警报(它应该有警报,查看输出)时

print(newEvent.hasAlarms)

it fails with 它失败了

 19170:5969208] -[__NSCFString hasAlarms]: unrecognized selector sent to instance 0x17425d340

This is tricky :( 这很棘手:(

EDIT : if in the SERIALIZER.SWITF class i add : extension EKEvent: EVReflectable { } then the newEvent is empty. 编辑:如果在SERIALIZER.SWITF类中添加:扩展名EKEvent:EVReflectable {},则newEvent为空。

..really tricky :D ..真的很棘手:D

Victor 胜利者

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM